#define	_ARH_TIPKOVNICA_C_
#include "tipkovnica.h"

#include "prekidi.h"
#include "razno.h"
#include "vj_sucelje.h"
#include "ispis.h"
#include "spremnik.h"

static bool prikazi_stisnuto;
//da li ispisati na zaslon svaku pritisnutu tipku? "echo"

static tipka_t ms[TIPKA_MS_VEL] = {0}; //koji su znakovi ucitani
//specijalni su znakovi u visim bitovima

static int ms_poc = 0;	//indeks prvog neprocitanog znaka u ms
static int ms_kraj = 0;	//indeks prvog praznog mjesta u ms
static int ms_br = 0;	//broj znakova u ms

static tipka_t spec_tipke;	//pamte se trenutno stisnute "specijalne tipke"

//dozvoli/zabrani proslijedjivanje prekida kontrolera tipkovnice preko PIC-a
void arh_dozvoli_prekid_tipkovnice ()
{
	i8259_omoguci_prekid(IRQ_KEYBOARD);
}
void arh_zabrani_prekid_tipkovnice ()
{
	i8259_zabrani_prekid(IRQ_KEYBOARD);
}


void arh_tipkovnica_inicijaliziraj (bool _prikazi_stisnuto)
{
	int i;

	prikazi_stisnuto = _prikazi_stisnuto;
	
	//isprazni buffer, ako tamo ima nesto
	while (arh_tipkovnica_procitaj() >= 0)
		;
	
	for (i = 0; i <TIPKA_MS_VEL ; i++)
		ms[i] = 0;

	ms_poc = ms_kraj = ms_br = 0;

	//jos nisu stisnute spec. tipke
	spec_tipke = 0;
}

int arh_tipkovnica_dohvati(zahtjev_tipkovnici *p)
{
	int c, pocetak, duljina, *znak = arh_puj(p->ms);
	bool delimiter;
	char *niz, *p_ms = arh_puj(p->ms);
	
	if (!p || !p->ms || p->ms_d < 1) {
		//arh_ispisi_rijec("\n(arh) Krivi parametri\n");
		return -1;
	}
	
	if (ms_br == 0) {
		//arh_ispisi_rijec("\n(arh) Prazan MS\n");
		return 0;
	}

	if (p->sto == ZNAK) { //ASCII ili i kontrolni, ako je "svaki" postavljen
		//ako se trazi "normalan" znak, preskoci "specijalne"
		while (!p->svaki && !(ms[ms_poc] & ZNAK_MASKA)) {
			ms_br--;

			INC_MOD(ms_poc);			
			
			if (ms_br == 0)
				return 0;
		}
		
		if (p->svaki || (ms[ms_poc] & ZNAK_MASKA)) {
			c = ms[ms_poc];
			if (!p->svaki)
				c &= ZNAK_MASKA;

			*znak = c; //stavi znak u medjuspremnik

			ms_br--;
			INC_MOD(ms_poc);			
			
			return 1;
		}
		else {
			//tu zapravo ne bi trebao doci !?
			return 0;
		}
	}
	
	//samo ASCII za "RIJEC" i "REDAK"! specijalne znakove ignoriram
	duljina = 0;
	delimiter = NEISTINA;
	pocetak = ms_poc;
	
	niz = (char *) p_ms;
	
	//pretpostavi da ima ... kopiraj, ali za sada ne mjenjaj stanje
	while (pocetak != ms_kraj && duljina < p->ms_d - 1) {
		c = ms[pocetak] & ZNAK_MASKA;

		INC_MOD(pocetak);

		if ((p->sto == RIJEC && (c == ' ' || c == '\t')) ||
					c == '\n' || c == '\r') {

			if (duljina == 0) //prvi je delimiter, preskoci
				continue;

			delimiter = ISTINA;

			break; //ima u ms nesto "dobrih" znakova
		}

		if (c)
			niz[duljina++] = c;
	}
	
	//ako ima rijec/redak, azuriraj brojace
	if (duljina > 0 && (delimiter || duljina >= p->ms_d - 1)) {
		ms_poc = pocetak;

		ms_br = (ms_kraj - ms_poc + TIPKA_MS_VEL) % TIPKA_MS_VEL;
		
		niz[duljina++] = 0; //oznaka kraj niza
		
		return duljina;
	}
	else {
		return 0;
	}
}

// Funkcija koja hvata prekide od tipkovnice. Unutar nje su zabranjeni prekidi.
bool arh_tipkovnica_obradi_prekid()
{
	int c;
	bool ima = NEISTINA;

	// sakuplja sve dok ima nesto u medjuspremniku tipkovnice
	while ((c = arh_tipkovnica_procitaj()) >= 0) {
		if (ms_br < TIPKA_MS_VEL) {
			ms[ms_kraj] = c | spec_tipke;

			INC_MOD(ms_kraj);			

			ms_br++;
			
			if ((c & ZNAK_MASKA) && prikazi_stisnuto)
				arh_ispisi_znak(c & ZNAK_MASKA);
			
			ima = ISTINA;
		}
		else {
			//nema mjesta u MS; "odbaci" sve neprocitano
			while ( (c = arh_tipkovnica_procitaj()) >= 0)
				;
		}
	}
	
	return ima;
}

//! prevodjenje iz keyboard_scan_code-a u ascii
static char tipkovnica_kodovi[128] = {
  0,  27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0','\'', '+','\b','\t',
'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', '{', '}','\n',   0, 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',   0,'\\', 'y', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '-',   0, '*',   0, ' ',   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.'
};

//! prevodjenje iz keyboard_scan_code-a u ascii sa stisnutim shiftom
static char tipkovnica_kodovi_shift[128] = {
  0,   0, '!', '"', '#', '$', '%', '&', '/', '(', ')', '=',   0, '*','\b','\t',
'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P', '[', ']','\n',   0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '"',   0,   0, '|', 'Y', 'X', 'C', 'V',
'B', 'N', 'M', ';', ':', '_',   0,   0,   0, ' ',   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.'
};

/*!
 * Primanje odgovora s tipkovnice
 * \returns procitani znak ili -1 ako nema odgovora
 */
static tipka_t arh_tipkovnica_procitaj ()
{
	if (!(inb(tipkovnica_SR) & 0x01))
		return -1;

	return tipkovnica_obradi(inb(tipkovnica_PR));
}

static tipka_t tipkovnica_obradi (int c)
{
	tipka_t tipka = (tipka_t) 0;
	//za sada ignoriram posebne tipke (ctrl, alt, ...)
	
	// da li je neka specijalna tipka ili
	// tipka za koju je bitno da li se drzi...
	switch (c) {
	case 0xBA:	// caps lock
		spec_tipke ^= CAPSL;
		tipka = STIPKA(TCAPSL);
		tipkovnica_postavi_ledice();
		break;

	case 0xAA:	//otpusteni lijevi shift
		spec_tipke &= (0xffffffff ^ LSHIFT);
		tipka = STIPKA(TLSHIFT);
		break;

	case 0x2A:	//stisnuti lijevi shift
		spec_tipke |= LSHIFT;				
		tipka = STIPKA(TLSHIFT);
		break;

	case 0xB6:	//otpusteni desni shift
		spec_tipke &= (0xffffffff ^ DSHIFT);
		tipka = STIPKA(TDSHIFT);
		break;

	case 0x36:	//stisnuti desni shift
		spec_tipke |= DSHIFT;				
		tipka = STIPKA(TDSHIFT);
		break;

	default:	//nesto drugo, mozda "normalna tipka", uz "shift"?
		if (c < 128 && (spec_tipke & (LSHIFT | DSHIFT | CAPSL)))
			tipka = (tipka_t) tipkovnica_kodovi_shift[c];
		else if (c < 128)
			tipka = (tipka_t) tipkovnica_kodovi[c];
		break;
	}

	return tipka;
}

static void tipkovnica_naredba(int nar) 
{
	// radno ceka na spremnost za slanje naredbe
	while (inb(tipkovnica_SR) & 0x02)
		;
	// posalje naredbu
	outb(tipkovnica_PR, (unsigned char)nar);
}

static void tipkovnica_postavi_ledice()
{
	tipkovnica_naredba(0xED);
	if (spec_tipke & CAPSL)
		tipkovnica_naredba(CAPSLED);
	else
		tipkovnica_naredba(0);
}
